Wang Haihua
🍈 🍉🍊 🍋 🍌
可以使用Python的Sympy库求微分方程的符号解,Scipy库求微分方程的数值解;本文介绍数值解。
odeint
¶Python 对常微分方程的数值求解是基于一阶方程进行的, 高阶微分方程必须化成一阶方程组, 通常采用龙格—库塔方法。scipy.integrate
模块的 odeint
函数求常微分方程的数值解, 其基本调用格式为:
$$
\text { sol=odeint(func, y0, t) }
$$
其中 func
是定义微分方程的函数或匿名函数, y0
是初始条件的序列, $\mathbf{t}$ 是 一个自变量取值的序列 (t 的第一个元素一定为初始时刻), 返回值 sol
是 对应于序列 $\mathrm{t}$ 中元素的数值解, 如果微分方程组中有 $n$ 个函数, 返回值 sol
是 $n$ 列的矩阵, 第 $i(i=1,2, \cdots, n)$ 列对应于第 $i$ 个函数的数值解。
求微分方程 $$ \left\{\begin{array}{l} y^{\prime}=-2 y+x^{2}+2 x, \\ y(1)=2 . \end{array}\right. $$ 在 $1 \leq x \leq 10$ 步长间隔为 $0.5$ 点上的数值解。
代码
from scipy.integrate import odeint
from numpy import arange
dy=lambda y, x: -2*y+x**2+2*x
x=arange(1, 10.5, 0.5)
sol=odeint(dy, 2, x)
print("x={}\n对应的数值解y={}".format(x, sol.T))
求下述微分方程的解 $$ \left\{\begin{array}{l} \frac{d^{2} y}{d x^{2}}+2 \frac{d y}{d x}+2 y=0 \\ y(0)=0, \quad y^{\prime}(0)=1 \end{array}\right. $$
解: 引进 $y_{1}=y, y_{2}=y^{\prime}$, 则可以把原来的二阶微分方程化为如下一阶 微分方程组: $$ \begin{cases}y_{1}^{\prime}=y_{2}, & y_{1}(0)=0, \\ y_{2}^{\prime}=-2 y_{1}-2 y_{2}, & y_{2}(0)=1 .\end{cases} $$ 求数值解和画图的程序如下:
from scipy.integrate import odeint
from sympy.abc import t
import numpy as np
import matplotlib.pyplot as plt
def Pfun(y,x):
y1, y2=y;
return np.array([y2, -2*y1-2*y2])
x=np.arange(0, 10, 0.1) #创建时间点
sol1=odeint(Pfun, [0.0, 1.0], x) #求数值解
plt.rc('font',size=16); plt.rc('font',family='SimHei')
plt.plot(x, sol1[:,0],'r*',label="数值解")
plt.plot(x, np.exp(-x)*np.sin(x), 'g', label="符号解曲线")
plt.legend()
plt.show()
Lorenz 模型的混沌效应。
Lorenz 模型是由美国气象学家 Lorenz 在研究大气运动时, 通过简化对流模型, 只保留 3 个变量提出的一个完全确定性的一阶自治常微分方程组 (不显含时间变量), 其方程为 $$ \left\{\begin{array}{l} \dot{x}=\sigma(y-x), \\ \dot{y}=\rho x-y-x z, \\ \dot{z}=x y-\beta z . \end{array}\right. $$ 其中, 参数 $\sigma$ 为 Prandtl 数, $\rho$ 为 Rayleigh 数, $\beta$ 为方向比。
代码
from scipy.integrate import odeint
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
def lorenz(w,t):
sigma=10; rho=28; beta=8/3
x, y, z=w;
return np.array([sigma*(y-x), rho*x-y-x*z, x*y-beta*z])
t=np.arange(0, 50, 0.01) #创建时间点
sol1=odeint(lorenz, [0.0, 1.0, 0.0], t) #第一个初值问题求解
sol2=odeint(lorenz, [0.0, 1.0001, 0.0], t) #第二个初值问题求解
plt.rc('font',size=16); plt.rc('text',usetex=True)
ax1=plt.subplot(121,projection='3d')
ax1.plot(sol1[:,0], sol1[:,1], sol1[:,2],'r')
ax1.set_xlabel(r'$x$'); ax1.set_ylabel(r'$y$'); ax1.set_zlabel(r'$z$')
ax2=plt.subplot(122,projection='3d')
ax2.plot(sol1[:,0]-sol2[:,0], sol1[:,1]-sol2[:,1], sol1[:,2]-sol2[:,2],'g')
ax2.set_xlabel(r'$x$'); ax2.set_ylabel(r'$y$'); ax2.set_zlabel(r'$z$')
plt.tight_layout()
plt.savefig("images/diff0602.png", dpi=500);
plt.show()
print("sol1=",sol1, '\n\n', "sol1-sol2=", sol1-sol2)
Lorenz 模型如今已经成为混沌领域的经典模型, 第一个混沌吸引子一 Lorenz 吸引子也是在这个系统中被发现的。系统中三个参数的选择对系统 会不会进入混沌状态起着重要的作用。下图 给出了 Lorenz 模型在 $\sigma=10, \rho=28, \beta=8 / 3$ 时系统的三维演化轨迹。由图 可见, 经 过长时间运行后, 系统只在三维空间的一个有限区域内运动, 即在三维相空 间里的测度为零。图 (A) 显示出我们经常听到的“蝴蝶效应”。图 (B) 给出了系统从两个靠的很近的初值出发(相差仅 0.0001)后, 解的偏差演化曲线。
随着时间的增大,可以看到两个解的差异越来越大,这正是动力学系统对初值敏感性的直观表现,由此可断定此系统的这种状态为混沌态。混沌运动是确定性系统中存在随机性,它的运动轨道对初始条件极端敏感。
from scipy.integrate import odeint
from numpy import arange
dy=lambda y, x: -2*y+x**2+2*x
x=arange(1, 10.5, 0.5)
sol=odeint(dy, 2, x)
print("x={}\n对应的数值解y={}".format(x, sol.T))
x=[ 1. 1.5 2. 2.5 3. 3.5 4. 4.5 5. 5.5 6. 6.5 7. 7.5 8. 8.5 9. 9.5 10. ] 对应的数值解y=[[ 2. 2.08484933 2.9191691 4.18723381 5.77289452 7.63342241 9.75309843 12.12613985 14.75041934 17.62515427 20.75005673 24.12502089 27.7500077 31.62500278 35.75000104 40.1250004 44.75000015 49.62500006 54.75000002]]
from scipy.integrate import odeint
from sympy.abc import t
import numpy as np
import matplotlib.pyplot as plt
def Pfun(y,x):
y1, y2=y;
return np.array([y2, -2*y1-2*y2])
x=np.arange(0, 10, 0.1) #创建时间点
sol1=odeint(Pfun, [0.0, 1.0], x) #求数值解
plt.rc('font',size=16); plt.rc('font',family='SimHei')
plt.plot(x, sol1[:,0],'r*',label="数值解")
plt.plot(x, np.exp(-x)*np.sin(x), 'g', label="符号解曲线")
plt.legend(); plt.savefig("images/diff0601.png"); plt.show()
from scipy.integrate import odeint
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
def lorenz(w,t):
sigma=10; rho=28; beta=8/3
x, y, z=w;
return np.array([sigma*(y-x), rho*x-y-x*z, x*y-beta*z])
t=np.arange(0, 50, 0.01) #创建时间点
sol1=odeint(lorenz, [0.0, 1.0, 0.0], t) #第一个初值问题求解
sol2=odeint(lorenz, [0.0, 1.0001, 0.0], t) #第二个初值问题求解
plt.rc('font',size=16); plt.rc('text',usetex=True)
ax1=plt.subplot(121,projection='3d')
ax1.plot(sol1[:,0], sol1[:,1], sol1[:,2],'r')
ax1.set_xlabel(r'$x$'); ax1.set_ylabel(r'$y$'); ax1.set_zlabel(r'$z$')
ax2=plt.subplot(122,projection='3d')
ax2.plot(sol1[:,0]-sol2[:,0], sol1[:,1]-sol2[:,1], sol1[:,2]-sol2[:,2],'g')
ax2.set_xlabel(r'$x$'); ax2.set_ylabel(r'$y$'); ax2.set_zlabel(r'$z$')
plt.tight_layout()
plt.savefig("images/diff0602.png", dpi=500);
plt.show()
print("sol1=",sol1, '\n\n', "sol1-sol2=", sol1-sol2)
sol1= [[ 0.00000000e+00 1.00000000e+00 0.00000000e+00] [ 9.51228522e-02 1.00353502e+00 4.79004160e-04] [ 1.82774668e-01 1.03241643e+00 1.86842924e-03] ... [ 6.09359691e+00 -2.92299103e+00 3.38259871e+01] [ 5.22166759e+00 -3.19224234e+00 3.27650429e+01] [ 4.41196211e+00 -3.36467046e+00 3.17470149e+01]] sol1-sol2= [[ 0.00000000e+00 -1.00000000e-04 0.00000000e+00] [-9.51228551e-06 -1.00353479e-04 -9.58055863e-08] [-1.82774502e-05 -1.03241296e-04 -3.73704777e-07] ... [ 1.65199550e+01 1.28275813e+00 -1.70072250e+00] [ 1.50178317e+01 2.41273736e-01 -2.20722691e+00] [ 1.35697202e+01 -5.93821006e-01 -2.59441701e+00]]